package tazmania.entity;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;

import tazadum.engine.Entity;
import tazadum.engine.Interval;
import tazadum.engine.Signal;
import tazadum.engine.trigger.GreaterThanTrigger;
import tazadum.engine.trigger.Trigger;
import tazmania.SparkLine;

public class SquareGrowerEntity implements Entity {
	private static final int MAX_SPAWNS = 100;;
	private final Signal signal;

	private final Trigger spawnSquare = new GreaterThanTrigger(0.25f, 0);

	private final int triggerChannel;
	private final int pulseChannel;

	private final List<Spawn> spawns = new ArrayList<Spawn>(MAX_SPAWNS);
	private int squareList;

	private final static float WIDTH = 640;
	private final static float HEIGHT = 480;

	private float spawnX;
	private float spawnY;
	private float spawnDx = 1;
	private float spawnDy = 0;
	private final float spawnStep = 20;

	public SquareGrowerEntity(final Signal signal, final int triggerChannel, final int pulseChannel) {
		this.signal = signal;
		this.triggerChannel = triggerChannel;
		this.pulseChannel = pulseChannel;
	}

	@Override
	public void precalc(final GLAutoDrawable drawable, final float screenRatio) {
		final GL2 gl = drawable.getGL().getGL2();

		squareList = gl.glGenLists(1);

		gl.glNewList(squareList, GL2.GL_COMPILE);
		gl.glBegin(GL2.GL_QUADS);
		{
			final float S = 10f;
			final float R = 1;
			gl.glVertex2f(-R * S, -S);
			gl.glVertex2f(R * S, -S);
			gl.glVertex2f(R * S, S);
			gl.glVertex2f(-R * S, S);
		}
		gl.glEnd();
		gl.glEndList();

		spawnX = spawnStep;
		spawnY = HEIGHT - spawnStep;
	}

	@Override
	public void update(final GLAutoDrawable drawable, final float globalTime, final float localTime, final float dt, final Interval interval) {
		final GL2 gl = drawable.getGL().getGL2();

		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glOrtho(0, WIDTH, HEIGHT, 0, 0.1f, 1000);
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glTranslatef(0.0f, 0.0f, -4.0f);

		gl.glDisable(GL.GL_DEPTH_TEST);
		gl.glDisable(GL2.GL_LIGHTING);

		float pulse = signal.channel(pulseChannel);
		if (globalTime > interval.from() + interval.length() * .5f) {
			pulse = 0;
		}
		final float trigger = signal.channel(triggerChannel);

		if (spawnSquare.trigger(trigger) && spawns.size() < MAX_SPAWNS) {
			spawns.add(new Spawn(spawnX, spawnY, trigger));
			spawns.add(new Spawn(WIDTH - spawnX, HEIGHT - spawnY, trigger));

			spawnX += spawnDx * spawnStep * 2;
			spawnY += spawnDy * spawnStep * 2;
			if (spawnX > WIDTH - spawnStep) {
				spawnX -= spawnStep * 2;
				spawnY -= spawnStep * 2;
				spawnDx = 0;
				spawnDy = -1;
			} else if (spawnY < spawnStep) {
				spawnY += spawnStep * 2;
				spawnX -= spawnStep * 2;
				spawnDx = -1;
				spawnDy = 0;
			} else if (spawnX < spawnStep) {
				spawnX += spawnStep * 2;
				spawnY += spawnStep * 2;
				spawnDx = 0;
				spawnDy = 1;
			} else if (spawnY > HEIGHT - spawnStep) {
				spawnX += spawnStep * 2;
				spawnY -= spawnStep * 2;
				spawnDx = 1;
				spawnDy = 0;
			}
		}

		if (!spawns.isEmpty()) {
			final Spawn last1 = spawns.get(spawns.size() - 1);
			if (last1.scale < trigger) {
				last1.scale = trigger;
				spawns.get(spawns.size() - 2).scale = trigger;
			}
		}

		final float R = signal.channel(Signal.CHANNEL_R);
		final float G = signal.channel(Signal.CHANNEL_G);
		final float B = signal.channel(Signal.CHANNEL_B);
		{
			gl.glLineWidth(1);
			final ListIterator<Spawn> iterator = spawns.listIterator();
			while (iterator.hasNext()) {
				final Spawn spawn = iterator.next();
				final float scale = spawn.scale;
				final float dx = (WIDTH / 2 - spawn.x) * 0.2f * pulse * spawn.scale;
				final float dy = (HEIGHT / 2 - spawn.y) * 0.2f * pulse * spawn.scale;

				spawn.x2 = spawn.x + dx;
				spawn.y2 = spawn.y + dy;

				gl.glPushMatrix();
				gl.glTranslatef(spawn.x2, spawn.y2, 0);
				gl.glScalef(scale, scale, scale);

				gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);
				gl.glColor4f(R, G, B, 1);
				gl.glCallList(squareList);
				gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_LINE);
				gl.glColor4f(0, 0, 0, 1);
				gl.glCallList(squareList);

				spawn.scale -= dt * .5f;
				if (spawn.scale <= 0) {
					iterator.remove();
				}

				gl.glPopMatrix();
			}
		}

		if (pulse > 0) {
			gl.glEnable(GL.GL_BLEND);
			gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
			gl.glColor4f(0, 0, 0, pulse);
			final ListIterator<Spawn> iterator = spawns.listIterator();
			while (iterator.hasNext()) {
				final Spawn from = iterator.next();
				final Spawn to = iterator.next();

				SparkLine.draw(gl, from.x2, from.y2, to.x2, to.y2, pulse);
			}
			gl.glDisable(GL.GL_BLEND);
		}

		// gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glPopMatrix();
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glPopMatrix();

		// for (int i = 0; i < 8; i++) {
		// System.out.print(signal.channel(Signal.CHANNEL_SYNTH_ENV_0 + i) + "\t");
		// }
		// System.out.println("");
	}

	private static class Spawn {
		public float y2;
		public float x2;
		public float x;
		public float y;
		public float scale;

		public Spawn(final float x, final float y, final float scale) {
			this.x = x;
			this.y = y;
			this.scale = scale;
		}

	}

	@Override
	public void destroy(final GLAutoDrawable drawable) {
	}
}
